Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Aqara Roller Driver E1 as v2 quirk to expose configuration and status entities #3686

Open
wants to merge 8 commits into
base: dev
Choose a base branch
from

Conversation

jeverley
Copy link
Contributor

@jeverley jeverley commented Jan 8, 2025

Proposed change

This PR refactors the existing Aqara Roller Driver E1 quirk into the new v2 structure to expose the configuration and status attributes, below is a summary of changes:

  • Refactor into v2 quirk
  • Refactor clusters to use AttributeDefs format
  • Represent the device as a Rollershade WindowCoveringType rather than Drapery
  • The current position is refreshed after a stop command is issued
  • Expose attributes as entities:
    • Motor speed (select)
    • Charging status (binary sensor)
    • Calibration status (binary sensor)

Functional enhancements

  • Users can change the motor speed in the device page
  • Users can change the motor speed using the Select action
  • Users can see if the device is charging in the device page
  • Users can see if the device is calibrated in the device page
  • The cover entity uses the Rollershade type
image

Additional information

Whilst I've validated that is works with my own E1 roller shade devices, I'd like to ensure other users do not encounter any regressions with this refactor.
@dmulcahey / @schwickster / @javicalle / @badrpc / @TheJulianJES I can see in the past you've previously contributed for this device, if you still use it would you mind checking if this updated quirk works as expected without introducing regressions?

Checklist

  • The changes are tested and work correctly
  • pre-commit checks pass / the code has been formatted using Black
  • Tests have been added to verify that the new code works

jeverley and others added 6 commits January 8, 2025 10:16
 - Refactor into v2 quirk 
 - Refactor clusters to use AttributeDefs format
 - Represent the device as a Rollershade rather than Drapery
 - Refresh current position after a stop command is issued
 - Expose attributes as entities:
	 - Motor speed (select)
	 - Charging status (binary sensor)
	 - Calibration status (binary sensor)
 - Update existing tests to use the v2 quirk
 - Add test for the charging status attribute value handling
Copy link

codecov bot commented Jan 8, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 89.87%. Comparing base (e3d3849) to head (5498f26).

Additional details and impacted files
@@            Coverage Diff             @@
##              dev    #3686      +/-   ##
==========================================
+ Coverage   89.85%   89.87%   +0.01%     
==========================================
  Files         322      322              
  Lines       10380    10400      +20     
==========================================
+ Hits         9327     9347      +20     
  Misses       1053     1053              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@jeverley jeverley marked this pull request as ready for review January 8, 2025 13:58
@TheJulianJES TheJulianJES added Xiaomi Request/PR regarding a Xiaomi device needs review This PR should be reviewed soon, as it generally looks good. labels Jan 8, 2025
@jeverley
Copy link
Contributor Author

jeverley commented Jan 8, 2025

Have identified an issue with the cover state not reliably changing to open/closed. Investigating this currently.

@jeverley
Copy link
Contributor Author

jeverley commented Jan 8, 2025

@TheJulianJES, @dmulcahey looking at the code in https://github.com/zigpy/zha/blob/dev/zha/application/platforms/cover/__init__.py
it looks to me like the _determine_state function and stored target positions might be the cause of the issue, I'm seeing similar issues reported for other devices which would appear to have the same root cause as below home-assistant/core#93836, home-assistant/core#98933.

I've tested the following scenarios:

1. go_to_lift_percentage has never been called, down_close is called, then the device reports position_or_tilt of 0
_target_lift_position == None
position_or_tilt == 0
returns STATE_CLOSED

2. go_to_lift_percentage has never been called, up_open is called, then the device reports position_or_tilt of 100
_target_lift_position == None
position_or_tilt == 100
returns: STATE_OPEN

3. go_to_lift_percentage is called (go to 0), then the device reports position_or_tilt of 0
_target_lift_position == 0
position_or_tilt == 0
returns STATE_CLOSED

4. go_to_lift_percentage is called (go to 100), then the device reports position_or_tilt of 100
_target_lift_position == 100
position_or_tilt == 100
returns STATE_OPEN

5. go_to_lift_percentage is called (go to 50), then the device reports position_or_tilt of 50
_target_lift_position == 50
position_or_tilt == 50
returns STATE_OPEN

6. go_to_lift_percentage has previously been called (stored value is 50), down_close is called, then the device reports position_or_tilt of 0
_target_lift_position == 50
position_or_tilt == 0
returns STATE_CLOSED

7. go_to_lift_percentage has previously been called (stored value is 50), up_open is called, then the device reports position_or_tilt of 100
_target_lift_position == 50
position_or_tilt == 100
returns None, state remains as STATE_OPENING

8. go_to_lift_percentage has previously been called (stored value is 50) and current position is 100, stop is called
_target_lift_position == 100 (the function async_stop_cover sets it to the current cover position)
position_or_tilt == 100
returns STATE_OPEN

I'd suggest we can handle this in one of 3 ways:

  1. set the stored _target_lift_position to 100 or 0 when calling the up_open or down_close commands respectively
  2. clear the stored _target_lift_position when calling the up_open or down_close commands
  3. modify the _determine_state function to return STATE_OPEN when self.is_opening == True and position_or_tilt == 100 is true

Below is the current function for reference:

    def _determine_state(self, position_or_tilt, is_lift_update=True) -> None:
        """Determine the state of the cover.

        In HA None is unknown, 0 is closed, 100 is fully open.
        In ZCL 0 is fully open, 100 is fully closed.
        Keep in mind the values have already been flipped to match HA
        in the WindowCovering cluster handler
        """
        if is_lift_update:
            target = self._target_lift_position
            current = self.current_cover_position
        else:
            target = self._target_tilt_position
            current = self.current_cover_tilt_position

        if position_or_tilt == 0:
            self._state = (
                STATE_CLOSED
                if is_lift_update
                else STATE_OPEN
                if self.current_cover_position is not None
                and self.current_cover_position > 0
                else STATE_CLOSED
            )
            return
        if target is not None and target != current:
            # we are mid transition and shouldn't update the state
            return
        self._state = STATE_OPEN

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs review This PR should be reviewed soon, as it generally looks good. Xiaomi Request/PR regarding a Xiaomi device
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants